#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
QNX系统进程监控工具

该脚本用于持续监控QNX系统中特定进程的CPU和内存使用情况，
通过ADB和telnet自动连接到设备并获取相关信息。
同时可以监控GPU使用率。
"""

import subprocess
import time
import re
import csv
import os
import shutil
import platform
import threading
import queue
from datetime import datetime

# 根据操作系统选择适当的库
if platform.system() == 'Windows':
    try:
        import wexpect

        # 创建一个别名，使代码兼容
        pexpect = wexpect
        IS_WINDOWS = True
        # Windows系统下，增加调试输出
        DEBUG_MODE = True


        # wexpect库没有exceptions模块，需要自定义异常处理
        class WexpectExceptionWrapper:
            class ExceptionPexpect(Exception):
                pass


        pexpect.exceptions = WexpectExceptionWrapper
    except ImportError:
        print("错误: 在Windows系统上需要安装wexpect库。")
        print("请运行: pip install wexpect")
        exit(1)
else:
    import pexpect

    IS_WINDOWS = False
    DEBUG_MODE = True

#################################################
################ 配置参数区域 ####################
#################################################

# 要监控的进程名称
PROCESS_NAME = "zone_clusterhmi"

# 数据采集间隔(秒)
INTERVAL = 3

# 设备类型选择
# "device1": 需要密码验证的设备 (默认)
# "device2": 只需账号不需密码的设备 (执行adb root后连接)
DEVICE_TYPE = "device2"

# 设备1配置 (需要密码验证)
DEVICE1_QNX_IP = "192.168.8.1"
DEVICE1_USERNAME = "root"
DEVICE1_PASSWORD = "sv2655888"

# 设备2配置 (只需账号无需密码)
DEVICE2_QNX_IP = "172.31.207.204"
DEVICE2_USERNAME = "root"

# 是否监控GPU使用率
MONITOR_GPU = False

# 输出文件路径，留空则自动生成文件名
OUTPUT_FILE = ""  # 例如: "logservice_monitor.csv"

# ADB路径设置
# 如果为空，将自动查找adb路径；否则使用指定的路径
# 例如: "/Users/username/Library/Android/sdk/platform-tools/adb"
ADB_PATH = ""


#################################################
################ 程序代码区域 ####################
#################################################

def find_adb_path():
    """
    查找adb命令的路径

    Returns:
        adb命令的完整路径，如果未找到则返回None
    """
    # 首先检查是否已经设置了ADB_PATH
    if ADB_PATH and os.path.exists(ADB_PATH) and os.access(ADB_PATH, os.X_OK):
        return ADB_PATH

    # 检查环境变量中的adb
    adb_from_path = shutil.which('adb')
    if adb_from_path:
        return adb_from_path

    # 检查常见的adb安装路径
    common_paths = [
        # macOS 常见路径
        '/usr/local/bin/adb',
        os.path.expanduser('~/Library/Android/sdk/platform-tools/adb'),
        '/Applications/Android Studio.app/Contents/sdk/platform-tools/adb',
        # Linux 常见路径
        '/usr/bin/adb',
        '/opt/android-sdk/platform-tools/adb',
        # Windows 常见路径
        'C:\\Program Files\\Android\\sdk\\platform-tools\\adb.exe',
        'C:\\Program Files (x86)\\Android\\sdk\\platform-tools\\adb.exe',
        # 用户自定义安装路径
        os.path.expanduser('~/android-sdk/platform-tools/adb'),
        os.path.expanduser('~/Android/Sdk/platform-tools/adb')
    ]

    for path in common_paths:
        if os.path.exists(path) and os.access(path, os.X_OK):
            return path

    return None


class QNXProcessMonitor:
    def __init__(self, process_name, interval=5, output_file=None, device_type="device1",
                 ip1="192.168.8.1", username1="root", password1="sv2655888",
                 ip2="172.31.207.204", username2="root", adb_path=None,
                 monitor_gpu=False):
        """
        初始化QNX进程监控器

        Args:
            process_name: 要监控的进程名
            interval: 监控间隔时间(秒)
            output_file: 输出CSV文件名(可选)
            device_type: 设备类型("device1"或"device2")
            ip1: 设备1的QNX系统IP地址
            username1: 设备1的登录用户名
            password1: 设备1的登录密码
            ip2: 设备2的QNX系统IP地址
            username2: 设备2的登录用户名
            adb_path: adb命令路径(可选)
            monitor_gpu: 是否监控GPU使用率
        """
        self.process_name = process_name
        self.interval = interval
        self.device_type = device_type
        self.ip1 = ip1
        self.username1 = username1
        self.password1 = password1
        self.ip2 = ip2
        self.username2 = username2
        self.adb_path = adb_path
        self.monitor_gpu = monitor_gpu
        self.gpu_monitor_started = False
        self.gpu_usage_data = None
        self.gpu_thread = None
        self.running = False
        self.gpu_queue = queue.Queue()
        self.last_gpu_usage = None  # 保存上一次的GPU使用率
        self.reset_gpu_monitor_flag = False  # 用于标记GPU监控是否需要重新启动

        # 设置输出文件
        if output_file:
            self.output_file = output_file
        else:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            self.output_file = f"{process_name}_monitor_{timestamp}.csv"

        # 创建CSV文件并写入表头
        with open(self.output_file, 'w', newline='') as csvfile:
            writer = csv.writer(csvfile)
            if self.monitor_gpu:
                writer.writerow(['时间戳', '进程名', 'PID', 'CPU使用率(%)', '总内存(KB)', '堆内存(KB)', 'GPU使用率(%)'])
            else:
                writer.writerow(['时间戳', '进程名', 'PID', 'CPU使用率(%)', '总内存(KB)', '堆内存(KB)'])

        print(f"监控数据将保存到: {self.output_file}")

    def connect_to_qnx_device1(self):
        """连接到需要密码验证的QNX系统并返回终端会话"""
        # 确保adb路径有效
        if not self.adb_path:
            raise ValueError("无法找到adb命令。请确保已安装adb并添加到PATH环境变量中，或在脚本中设置ADB_PATH。")

        # 启动ADB shell
        print(f"使用adb路径: {self.adb_path}")
        try:
            if DEBUG_MODE:
                print("DEBUG: 尝试启动adb shell...")
            adb_shell = pexpect.spawn(f'{self.adb_path} shell', timeout=15)
            if DEBUG_MODE:
                print("DEBUG: adb shell已启动")
        except Exception as e:
            if IS_WINDOWS:
                error_msg = f"启动adb shell失败: {str(e)}。请检查adb是否正确安装，设备是否连接。"
            else:
                error_msg = f"启动adb shell失败: {str(e)}。请检查adb是否正确安装，设备是否连接。"
            if DEBUG_MODE:
                print(f"DEBUG: {error_msg}")
            raise RuntimeError(error_msg)

        if DEBUG_MODE:
            print("DEBUG: 等待adb shell提示符...")
        index = adb_shell.expect(['#', '>', pexpect.TIMEOUT, pexpect.EOF], timeout=15)
        if index == 2:
            if DEBUG_MODE:
                print("DEBUG: adb shell连接超时")
            raise TimeoutError("adb shell连接超时。请检查设备是否已连接并开启USB调试。")
        elif index == 3:
            if DEBUG_MODE:
                print("DEBUG: adb shell连接失败")
            raise ConnectionError("adb shell连接失败。请检查设备是否已连接并开启USB调试。")

        if DEBUG_MODE:
            print(f"DEBUG: adb shell已连接，提示符索引: {index}")

        # 启动telnet连接
        print(f"连接到QNX系统 {self.ip1}...")
        if DEBUG_MODE:
            print(f"DEBUG: 发送命令: busybox telnet {self.ip1}")
        adb_shell.sendline(f'busybox telnet {self.ip1}')

        # 等待登录提示
        if DEBUG_MODE:
            print("DEBUG: 等待telnet登录提示...")
        index = adb_shell.expect(['login:', 'Connection refused', pexpect.TIMEOUT], timeout=15)
        if index == 1:
            if DEBUG_MODE:
                print("DEBUG: telnet连接被拒绝")
            raise ConnectionError(f"telnet连接被拒绝。请检查IP地址 {self.ip1} 和网络连接。")
        elif index == 2:
            if DEBUG_MODE:
                print("DEBUG: telnet连接超时")
            raise TimeoutError(f"telnet连接超时。请检查设备 {self.ip1} 是否可达。")

        if DEBUG_MODE:
            print("DEBUG: 收到登录提示")

        # 登录
        print(f"使用用户名 {self.username1} 登录...")
        adb_shell.sendline(self.username1)
        if DEBUG_MODE:
            print("DEBUG: 等待密码提示...")
        adb_shell.expect('Password:', timeout=15)
        if DEBUG_MODE:
            print("DEBUG: 收到密码提示，发送密码...")
        adb_shell.sendline(self.password1)

        # 确认登录成功
        if DEBUG_MODE:
            print("DEBUG: 等待登录确认...")
        index = adb_shell.expect(['#', '>', 'Login incorrect', pexpect.TIMEOUT], timeout=10)
        if index == 2:
            if DEBUG_MODE:
                print("DEBUG: 登录失败")
            raise ValueError("登录失败。请检查用户名和密码。")
        elif index == 3:
            if DEBUG_MODE:
                print("DEBUG: 登录超时")
            raise TimeoutError("登录后无响应。请检查设备状态。")

        if DEBUG_MODE:
            print(f"DEBUG: 登录成功，提示符索引: {index}")

        print("成功连接到QNX系统")
        return adb_shell

    def connect_to_qnx_device2(self):
        """连接到只需账号不需密码的QNX系统并返回终端会话"""
        # 确保adb路径有效
        if not self.adb_path:
            raise ValueError("无法找到adb命令。请确保已安装adb并添加到PATH环境变量中，或在脚本中设置ADB_PATH。")

        # 尝试执行adb root，但不强制要求成功
        print(f"尝试执行adb root...")
        try:
            if DEBUG_MODE:
                print("DEBUG: 尝试执行adb root...")
            root_process = pexpect.spawn(f'{self.adb_path} root', timeout=10)
            root_output = root_process.expect([pexpect.EOF, pexpect.TIMEOUT], timeout=10)
            if IS_WINDOWS:
                root_response = root_process.before
            else:
                root_response = root_process.before.decode('utf-8', errors='ignore')

            root_process.close()

            # 检查root命令输出
            if isinstance(root_response, str) and "cannot run as root" in root_response:
                print("警告: 设备不支持adb root权限，将尝试直接连接")
            else:
                # 给设备一些时间重启adbd
                if DEBUG_MODE:
                    print("DEBUG: adb root执行完成，等待设备重启adbd...")
                time.sleep(3)
        except Exception as e:
            if DEBUG_MODE:
                print(f"DEBUG: adb root执行失败: {str(e)}，将尝试直接连接")
            print(f"警告: adb root执行失败: {str(e)}，将尝试直接连接")

        # 启动ADB shell
        print(f"启动adb shell...")
        try:
            if DEBUG_MODE:
                print("DEBUG: 尝试启动adb shell...")
            # 为Windows系统设置更长的超时时间
            shell_timeout = 30 if IS_WINDOWS else 15
            adb_shell = pexpect.spawn(f'{self.adb_path} shell', timeout=shell_timeout)
            if DEBUG_MODE:
                print("DEBUG: adb shell已启动")
        except Exception as e:
            if DEBUG_MODE:
                print(f"DEBUG: adb shell启动失败: {str(e)}")
            raise RuntimeError(f"启动adb shell失败: {str(e)}。请检查adb是否正确安装，设备是否连接。")

        # 在Windows系统上，添加一个小的延迟以确保shell已准备好
        if IS_WINDOWS:
            time.sleep(2)
            if DEBUG_MODE:
                print("DEBUG: Windows系统，等待2秒确保shell准备就绪")

        if DEBUG_MODE:
            print("DEBUG: 等待adb shell提示符...")

        # 增加更多可能的提示符模式，特别是对Windows系统
        prompts = ['#', '>', '$', '\$', '%', '/ $', '/$ ', ':/ $', ':/$', ':/ #', ':/# ', '\\S+@\\S+:.* [#$]']
        timeout = 30 if IS_WINDOWS else 15

        # 首先打印一个空行，有时候可以触发提示符显示
        adb_shell.sendline('')

        if DEBUG_MODE:
            print(f"DEBUG: 使用以下提示符模式: {prompts}，超时时间: {timeout}秒")

        # 尝试匹配可能的提示符
        index = -1
        try:
            index = adb_shell.expect(prompts + [pexpect.TIMEOUT, pexpect.EOF], timeout=timeout)
            if DEBUG_MODE:
                print(f"DEBUG: 匹配到提示符，索引为: {index}")
        except Exception as e:
            if DEBUG_MODE:
                print(f"DEBUG: 匹配提示符时出错: {str(e)}")

        # 检查匹配结果
        if index >= len(prompts):  # 意味着匹配到了TIMEOUT或EOF
            if index == len(prompts):  # TIMEOUT
                if DEBUG_MODE:
                    print("DEBUG: adb shell连接超时，尝试发送回车键...")

                # 如果超时，再尝试发送一个回车键
                adb_shell.sendline('')
                try:
                    index = adb_shell.expect(prompts + [pexpect.TIMEOUT, pexpect.EOF], timeout=10)
                    if index < len(prompts):
                        if DEBUG_MODE:
                            print(f"DEBUG: 发送回车后匹配到提示符，索引为: {index}")
                    else:
                        if DEBUG_MODE:
                            print("DEBUG: 发送回车后仍然超时或EOF")
                        raise TimeoutError("adb shell连接超时。请检查设备是否已连接并开启USB调试。")
                except:
                    raise TimeoutError("adb shell连接超时。请检查设备是否已连接并开启USB调试。")
            else:  # EOF
                if DEBUG_MODE:
                    print("DEBUG: adb shell连接失败 (EOF)")
                raise ConnectionError("adb shell连接失败。请检查设备是否已连接并开启USB调试。")

        if DEBUG_MODE:
            print(f"DEBUG: adb shell已连接，提示符索引: {index}")

        # 启动telnet连接
        print(f"连接到QNX系统 {self.ip2}...")
        if DEBUG_MODE:
            print(f"DEBUG: 发送命令: busybox telnet {self.ip2}")
        adb_shell.sendline(f'busybox telnet {self.ip2}')

        # 等待登录提示
        if DEBUG_MODE:
            print("DEBUG: 等待telnet登录提示...")
        telnet_timeout = 30 if IS_WINDOWS else 15
        index = adb_shell.expect(['login:', 'Connection refused', pexpect.TIMEOUT], timeout=telnet_timeout)
        if index == 1:
            if DEBUG_MODE:
                print("DEBUG: telnet连接被拒绝")
            raise ConnectionError(f"telnet连接被拒绝。请检查IP地址 {self.ip2} 和网络连接。")
        elif index == 2:
            if DEBUG_MODE:
                print("DEBUG: telnet连接超时")
            raise TimeoutError(f"telnet连接超时。请检查设备 {self.ip2} 是否可达。")

        if DEBUG_MODE:
            print("DEBUG: 收到登录提示")

        # 登录(只需要用户名，不需要密码)
        print(f"使用用户名 {self.username2} 登录...")
        adb_shell.sendline(self.username2)

        # 确认登录成功，这里不会提示输入密码
        if DEBUG_MODE:
            print("DEBUG: 等待登录确认...")
        login_timeout = 30 if IS_WINDOWS else 10
        index = adb_shell.expect(['#', '>', 'Login incorrect', pexpect.TIMEOUT], timeout=login_timeout)
        if index == 2:
            if DEBUG_MODE:
                print("DEBUG: 登录失败")
            raise ValueError("登录失败。请检查用户名。")
        elif index == 3:
            if DEBUG_MODE:
                print("DEBUG: 登录超时")
            raise TimeoutError("登录后无响应。请检查设备状态。")

        if DEBUG_MODE:
            print(f"DEBUG: 登录成功，提示符索引: {index}")

        print("成功连接到QNX系统")
        return adb_shell

    def connect_to_qnx(self):
        """根据设备类型选择适当的连接方法"""
        if self.device_type == "device1":
            return self.connect_to_qnx_device1()
        elif self.device_type == "device2":
            return self.connect_to_qnx_device2()
        else:
            raise ValueError(f"不支持的设备类型: {self.device_type}。请使用 'device1' 或 'device2'。")

    def get_process_info(self, terminal):
        """获取指定进程的CPU和内存信息"""
        # 获取进程的PID和CPU使用率
        terminal.sendline('top -i 1 -d')
        terminal.expect(['#', '>'])

        # 根据操作系统处理不同的输出格式
        if IS_WINDOWS:
            # Windows上wexpect返回的是字符串，不需要decode
            top_output = terminal.before
        else:
            # 非Windows系统上pexpect返回的是bytes，需要decode
            top_output = terminal.before.decode('utf-8', errors='ignore')

        # 根据示例数据调整正则表达式
        # 示例格式: 181600357     2  10 Sem       1:01:32   1.69% qvm
        pid_pattern = re.compile(r'(\d+)\s+\d+\s+\d+\s+\w+\s+[\d:]+\s+([\d.]+)%\s+' + re.escape(self.process_name))

        # 在整个top输出中查找进程信息
        pid_match = None
        for line in top_output.splitlines():
            match = pid_pattern.search(line)
            if match and self.process_name in line:
                pid_match = match
                break

        if not pid_match:
            print(f"警告: 在top命令输出中未找到进程 '{self.process_name}'，尝试使用pidin命令...")

            # 使用pidin命令作为备选方案
            terminal.sendline(f'pidin arg | grep {self.process_name}')
            terminal.expect(['#', '>'])

            # 处理pidin命令输出
            if IS_WINDOWS:
                pidin_output = terminal.before
            else:
                pidin_output = terminal.before.decode('utf-8', errors='ignore')

            # 示例格式: " 1204298 zone_clusterhmi"
            # 需要排除grep命令本身的行
            pidin_pattern = re.compile(r'\s*(\d+)\s+' + re.escape(self.process_name) + r'\s*$')

            pid = None
            for line in pidin_output.splitlines():
                match = pidin_pattern.search(line)
                # 确保匹配到的不是grep命令本身
                if match and f"grep {self.process_name}" not in line:
                    pid = match.group(1)
                    break

            if not pid:
                print(f"警告: 未找到进程 '{self.process_name}'")
                return None

            # 使用pidin命令找到进程时，CPU使用率设为0
            cpu_usage = "0"
        else:
            pid = pid_match.group(1)
            cpu_usage = pid_match.group(2)

        # 获取内存信息
        terminal.sendline(f'showmem -p {pid}')
        terminal.expect(['#', '>'])

        # 根据操作系统处理不同的输出格式
        if IS_WINDOWS:
            # Windows上wexpect返回的是字符串，不需要decode
            mem_output = terminal.before
        else:
            # 非Windows系统上pexpect返回的是bytes，需要decode
            mem_output = terminal.before.decode('utf-8', errors='ignore')

        # 根据示例数据调整正则表达式
        # 示例格式: logservice |        2326601 |          29444 |          30328 |            128 |           3064 |            428 |          17936 |                 0 |           8772 |
        mem_pattern = re.compile(r'\s+' + re.escape(self.process_name) + r'\s+\|\s+' + re.escape(
            pid) + r'\s+\|\s+\d+\s+\|\s+(\d+)\s+\|\s+\d+\s+\|\s+\d+\s+\|\s+\d+\s+\|\s+(\d+)\s+\|')

        mem_match = None
        for line in mem_output.splitlines():
            match = mem_pattern.search(line)
            if match:
                mem_match = match
                break

        if not mem_match:
            # 尝试使用一个更宽松的正则表达式
            alt_mem_pattern = re.compile(
                r'\s+\S+\s+\|\s+' + re.escape(pid) + r'\s+\|[^|]+\|\s+(\d+)\s+\|[^|]+\|[^|]+\|[^|]+\|\s+(\d+)\s+\|')

            for line in mem_output.splitlines():
                match = alt_mem_pattern.search(line)
                if match:
                    mem_match = match
                    break

        if not mem_match:
            # 如果仍然未匹配，则尝试匹配最后一行的总内存使用
            total_mem_pattern = re.compile(r'Total process memory in use : (\d+)\(KB\)')
            total_match = total_mem_pattern.search(mem_output)

            if total_match:
                total_mem = total_match.group(1)
                heap_mem = "N/A"
            else:
                print(f"警告: 无法获取进程 '{self.process_name}' (PID: {pid}) 的内存信息")
                total_mem = "N/A"
                heap_mem = "N/A"
        else:
            total_mem = mem_match.group(1)
            heap_mem = mem_match.group(2)

        return {
            'pid': pid,
            'cpu_usage': cpu_usage,
            'total_mem': total_mem,
            'heap_mem': heap_mem
        }

    def reset_gpu_monitoring(self):
        """
        重置GPU监控状态，清除旧数据，确保重新启动时能获取新数据
        """
        self.gpu_monitor_started = False
        self.last_gpu_usage = None

        # 清空GPU数据队列
        while not self.gpu_queue.empty():
            try:
                self.gpu_queue.get_nowait()
            except:
                pass

        print("已重置GPU监控状态")

    def setup_gpu_monitoring(self, terminal):
        """设置GPU监控"""
        if self.monitor_gpu:
            print("初始化GPU监控...")
            # 先尝试终止可能存在的GPU监控进程
            try:
                terminal.sendline('\x03')  # 发送Ctrl+C
                terminal.expect(['#', '>', pexpect.TIMEOUT], timeout=1.0)

                # 发送一些清理命令
                terminal.sendline('pkill -f "slog2info -b KGSL"')
                terminal.expect(['#', '>', pexpect.TIMEOUT], timeout=1.0)
            except:
                pass

            # 发送GPU监控初始化命令
            terminal.sendline('echo gpu_set_log_level 4 > /dev/kgsl-control')
            terminal.expect(['#', '>'])
            terminal.sendline('echo gpubusystats 1000 > /dev/kgsl-control')
            terminal.expect(['#', '>'])
            terminal.sendline('echo gpu_number_of_active_contexts > /dev/kgsl-control')
            terminal.expect(['#', '>'])
            terminal.sendline('echo gpu_per_process_busy 1000 > /dev/kgsl-control')
            terminal.expect(['#', '>'])

            # 标记GPU监控已初始化
            self.gpu_monitor_started = True
            print("GPU监控已初始化")

    def gpu_monitor_thread(self):
        """
        GPU监控线程，单独启动一个telnet连接获取GPU数据
        """
        try:
            # 创建一个新的终端连接用于GPU监控
            gpu_terminal = self.connect_to_qnx()

            # 设置GPU监控命令
            self.setup_gpu_monitoring(gpu_terminal)

            # 启动持续监控命令
            print("启动GPU持续监控...")
            gpu_terminal.sendline('slog2info -b KGSL -W | grep busy')

            # 持续读取GPU数据
            consecutive_failures = 0
            max_failures = 5  # 连续失败5次后重新初始化

            while self.running:
                try:
                    # 检查是否需要重置GPU监控
                    if self.reset_gpu_monitor_flag:
                        print("检测到GPU监控重置标记，重新初始化GPU监控...")
                        # 发送Ctrl+C终止当前命令
                        gpu_terminal.sendline('\x03')
                        gpu_terminal.expect(['#', '>', pexpect.TIMEOUT], timeout=2.0)

                        # 重新设置GPU监控
                        self.setup_gpu_monitoring(gpu_terminal)
                        gpu_terminal.sendline('slog2info -b KGSL -W | grep busy')

                        # 重置标记和失败计数
                        self.reset_gpu_monitor_flag = False
                        consecutive_failures = 0

                        # 清除旧数据
                        self.last_gpu_usage = None
                        while not self.gpu_queue.empty():
                            try:
                                self.gpu_queue.get_nowait()
                            except:
                                pass

                        print("GPU监控已重新初始化")

                    # 设置一个短的超时时间，以便能够响应停止信号和重置信号
                    index = gpu_terminal.expect(['percentage busy = ([\d.]+)%', pexpect.TIMEOUT], timeout=1.0)
                    if index == 0:
                        # 获取匹配组，提取GPU使用率
                        try:
                            if IS_WINDOWS:
                                # 在Windows上，直接使用match对象
                                gpu_usage = float(gpu_terminal.match.group(1))
                            else:
                                # 在其他平台上，使用match方法
                                gpu_usage = float(gpu_terminal.match.group(1).decode('utf-8'))

                            # 保留两位小数
                            gpu_usage = round(gpu_usage, 2)

                            # 将GPU使用率放入队列，供主线程使用
                            # 只保留最新的值
                            while not self.gpu_queue.empty():
                                self.gpu_queue.get()
                            self.gpu_queue.put(gpu_usage)

                            # 重置失败计数
                            consecutive_failures = 0

                            if DEBUG_MODE:
                                print(f"DEBUG: GPU使用率: {gpu_usage}%")
                        except Exception as e:
                            if DEBUG_MODE:
                                print(f"DEBUG: 解析GPU数据异常: {str(e)}")
                            consecutive_failures += 1
                    else:
                        # 超时，增加连续失败计数
                        consecutive_failures += 1
                        if DEBUG_MODE:
                            print("DEBUG: GPU数据读取超时")

                    # 如果连续失败次数过多，尝试重新初始化GPU监控
                    if consecutive_failures >= max_failures:
                        if DEBUG_MODE:
                            print(f"DEBUG: GPU监控连续{consecutive_failures}次失败，尝试重新初始化")

                        # 尝试发送Ctrl+C停止当前命令
                        gpu_terminal.sendline('\x03')
                        gpu_terminal.expect(['#', '>', pexpect.TIMEOUT], timeout=2.0)

                        # 重新启动GPU监控
                        self.setup_gpu_monitoring(gpu_terminal)
                        gpu_terminal.sendline('slog2info -b KGSL -W | grep busy')

                        # 重置失败计数
                        consecutive_failures = 0

                        if DEBUG_MODE:
                            print("DEBUG: GPU监控已重新初始化")

                except Exception as e:
                    if DEBUG_MODE:
                        print(f"DEBUG: GPU监控线程异常: {str(e)}")
                    consecutive_failures += 1
                    time.sleep(1.0)

        except Exception as e:
            print(f"GPU监控线程错误: {str(e)}")
            # 尝试重新启动GPU监控线程
            if self.running:
                print("尝试重新启动GPU监控线程...")
                time.sleep(5)  # 等待5秒后重新启动
                threading.Thread(target=self.gpu_monitor_thread, daemon=True).start()
        finally:
            # 确保在线程结束时关闭终端连接
            if 'gpu_terminal' in locals():
                try:
                    gpu_terminal.sendline('\x03')  # 发送Ctrl+C终止当前命令
                    time.sleep(0.5)
                    gpu_terminal.sendline('pkill -f "slog2info -b KGSL"')  # 尝试杀死任何正在运行的slog2info进程
                    time.sleep(0.5)
                    gpu_terminal.sendline('exit')
                    gpu_terminal.close()
                except:
                    pass
            print("GPU监控线程已终止")

    def get_latest_gpu_usage(self):
        """
        获取最新的GPU使用率，当队列为空时保留上一次的值
        """
        if not self.monitor_gpu:
            return None

        try:
            # 获取队列中最新的GPU使用率，如果队列为空则返回上一次的值
            if not self.gpu_queue.empty():
                self.last_gpu_usage = self.gpu_queue.get_nowait()
            return self.last_gpu_usage
        except queue.Empty:
            return self.last_gpu_usage if hasattr(self, 'last_gpu_usage') else None
        except Exception:
            return None

    def start_monitoring(self):
        """开始持续监控进程"""
        try:
            # 重置GPU监控状态
            self.reset_gpu_monitoring()

            # 标记监控状态为运行中
            self.running = True

            # 连接到QNX系统
            terminal = self.connect_to_qnx()

            # 如果需要监控GPU，启动单独的GPU监控线程
            if self.monitor_gpu:
                print("启动GPU监控线程...")
                self.gpu_thread = threading.Thread(target=self.gpu_monitor_thread)
                self.gpu_thread.daemon = True
                self.gpu_thread.start()

                # 给GPU线程一些时间初始化
                time.sleep(6)

            print(f"开始监控进程 '{self.process_name}'，每 {self.interval} 秒采集一次数据...")
            print("按 Ctrl+C 停止监控")

            # 记录上一次成功获取的进程信息，用于在临时获取失败时使用
            last_process_info = None
            consecutive_failures = 0
            max_failures = 5  # 连续失败5次后重新连接

            while True:
                try:
                    # 获取进程信息
                    process_info = self.get_process_info(terminal)

                    # 如果成功获取进程信息，更新最后一次成功的信息并重置失败计数
                    if process_info:
                        last_process_info = process_info
                        consecutive_failures = 0
                    else:
                        # 进程信息获取失败，使用上一次的信息
                        if last_process_info:
                            process_info = last_process_info
                            print(f"警告: 使用上一次获取的进程信息")
                        consecutive_failures += 1

                    # 如果连续失败次数过多，尝试重新连接
                    if consecutive_failures >= max_failures:
                        print(f"连续{consecutive_failures}次获取进程信息失败，尝试重新连接...")
                        try:
                            terminal.sendline('exit')
                            terminal.close()
                        except:
                            pass

                        # 重新连接
                        terminal = self.connect_to_qnx()
                        consecutive_failures = 0
                        print("重新连接成功")

                    # 如果启用了GPU监控，获取最新的GPU使用率
                    gpu_usage = None
                    if self.monitor_gpu:
                        gpu_usage = self.get_latest_gpu_usage()

                    if process_info:
                        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

                        # 打印到控制台
                        console_output = f"[{timestamp}] 进程: {self.process_name}, PID: {process_info['pid']}, " \
                                         f"CPU: {process_info['cpu_usage']}%, 总内存: {process_info['total_mem']}KB, " \
                                         f"堆内存: {process_info['heap_mem']}KB"

                        if gpu_usage is not None:
                            console_output += f", GPU: {gpu_usage}%"

                        print(console_output)

                        # 写入CSV文件
                        with open(self.output_file, 'a', newline='') as csvfile:
                            writer = csv.writer(csvfile)
                            if self.monitor_gpu:
                                writer.writerow([
                                    timestamp,
                                    self.process_name,
                                    process_info['pid'],
                                    process_info['cpu_usage'],
                                    process_info['total_mem'],
                                    process_info['heap_mem'],
                                    gpu_usage if gpu_usage is not None else "N/A"
                                ])
                            else:
                                writer.writerow([
                                    timestamp,
                                    self.process_name,
                                    process_info['pid'],
                                    process_info['cpu_usage'],
                                    process_info['total_mem'],
                                    process_info['heap_mem']
                                ])
                except Exception as e:
                    print(f"监控循环异常: {str(e)}")
                    consecutive_failures += 1

                    # 如果连续失败次数过多，尝试重新连接
                    if consecutive_failures >= max_failures:
                        print(f"连续{consecutive_failures}次异常，尝试重新连接...")
                        try:
                            terminal.sendline('exit')
                            terminal.close()
                        except:
                            pass

                        # 重新连接
                        try:
                            terminal = self.connect_to_qnx()
                            consecutive_failures = 0
                            print("重新连接成功")
                        except Exception as e:
                            print(f"重新连接失败: {str(e)}")
                            time.sleep(5)  # 重连失败，等待一段时间后重试

                # 等待指定的间隔时间
                time.sleep(self.interval)

        except KeyboardInterrupt:
            print("\n监控已停止")
        except Exception as e:
            print(f"发生错误: {str(e)}")
        finally:
            # 标记监控状态为已停止
            self.running = False

            # 等待GPU监控线程结束
            if self.monitor_gpu and self.gpu_thread and self.gpu_thread.is_alive():
                print("等待GPU监控线程结束...")
                self.gpu_thread.join(timeout=5)

            # 确保杀死所有相关进程
            try:
                # 关闭终端连接
                if 'terminal' in locals():
                    terminal.sendline('pkill -f "slog2info -b KGSL"')  # 尝试杀死任何正在运行的slog2info进程
                    time.sleep(1)
                    terminal.sendline('exit')
                    terminal.close()
            except:
                pass

            print(f"监控数据已保存到: {self.output_file}")


def main():
    """主函数，使用内部配置参数启动监控"""
    try:
        # 查找adb路径
        adb_path = find_adb_path()
        if not adb_path:
            print("错误: 无法找到adb命令。")
            print("请尝试以下解决方案:")
            print("1. 确保已安装Android Debug Bridge (adb)")
            print("2. 将adb添加到系统PATH环境变量中")
            print("3. 在脚本配置区域设置ADB_PATH变量为adb的完整路径")
            print("\n可能的adb路径位置:")
            print("- macOS: ~/Library/Android/sdk/platform-tools/adb")
            print("- Linux: /usr/bin/adb 或 /opt/android-sdk/platform-tools/adb")
            print("- Windows: C:\\Program Files\\Android\\sdk\\platform-tools\\adb.exe")
            return

        # 根据设备类型设置参数
        if DEVICE_TYPE == "device1":
            print("使用设备类型1: 需要密码验证的设备")
            ip = DEVICE1_QNX_IP
            username = DEVICE1_USERNAME
            password = DEVICE1_PASSWORD
        elif DEVICE_TYPE == "device2":
            print("使用设备类型2: 只需账号不需密码的设备")
            ip = DEVICE2_QNX_IP
            username = DEVICE2_USERNAME
            password = None
        else:
            print(f"错误: 不支持的设备类型 '{DEVICE_TYPE}'。请使用 'device1' 或 'device2'。")
            return

        # 使用配置参数决定是否监控GPU
        monitor_gpu = MONITOR_GPU

        monitor = QNXProcessMonitor(
            process_name=PROCESS_NAME,
            interval=INTERVAL,
            output_file=OUTPUT_FILE,
            device_type=DEVICE_TYPE,
            ip1=DEVICE1_QNX_IP,
            username1=DEVICE1_USERNAME,
            password1=DEVICE1_PASSWORD,
            ip2=DEVICE2_QNX_IP,
            username2=DEVICE2_USERNAME,
            adb_path=adb_path,
            monitor_gpu=monitor_gpu
        )

        monitor.start_monitoring()
    except Exception as e:
        print(f"启动监控失败: {str(e)}")


if __name__ == '__main__':
    main()
